/*
 * Operation.cpp
 *
 *  Created on: Jul 24, 2011
 *      Author: ofrenk33
 */

#include "Operation.h"
using namespace std;
using namespace OpenBabel;

//assumption: we already parse the operation name, but argv still contain it.
//TODO _findPerm and _usePerm need to be handle according to the flags in the input!
Operation::Operation(int argc, char *argv[]) : _findPerm(false), _usePerm(false), _timeOnly(false){
	assert(argc>=4); //suppose to check this in the main program

	// try to open infile for reading
	_inFileName = argv[2];
	if ((_inFile = fopen(argv[2], "rt")) == NULL){//TODO exceptions?
		cerr << "Error: Failed to open data file" << argv[2] << endl;
		exit(1);
	}

	readAndCreateMolecule();

	// try to open outfile for writing
	if ((_outFile = fopen(argv[3], "w")) == NULL){//TODO exceptions?
		cerr << "Error: Failed to open output file " << argv[3] << " for writing" << endl;
		exit(1);
	}

	if (_findPerm && _usePerm) {//TODO exceptions?
		cerr << "-findperm and -useperm can't be used together" << endl;
		exit(1);
	}

}

void Operation::readAndCreateMolecule() {
	OBMol babmol = OBMol();
	OBConversion conv;
	OBFormat* f = OBConversion::FormatFromExt(_inFileName);
	const char *format;
	if (f == NULL) {//TODO exceptions for all this section?
		cerr << "Error discovering format from filename: " << _inFileName << endl;
		exit(1);
	}
	format = OBConversion::FormatFromExt(_inFileName)->GetType().name();
	if (!conv.SetInFormat(OBConversion::FormatFromExt(_inFileName))) {
		cerr << "Error setting input format to: " << format << endl;
		exit(1);
	}
	if (!conv.ReadFile(&babmol, _inFileName)) {
		cerr << "Error reading file " << _inFileName << " using OpenBabel with format "
				<< format << endl;;
		exit(1);
	}
	_mol = new Molecule(babmol);
}

void Operation::estimateRemainingTime(){
	if (!_findPerm) {
		if (!_usePerm) {
			double time = 1.0*numPermutations() / 3600 / APPROX_RUN_PER_SEC;
			if (time != time) {
				// time is NaN
				time = MAXDOUBLE;
			}
			cout << "Going to enumerate over " << numPermutations() << " permutations" << endl;
			cout << "Entire run should take approx. " << time << " hours on a 2.0Ghz Computer" << endl;
		} else {
			cout << "Going to enumerate over 1.0 permutations" << endl;
			cout << "Entire run should take approx. " << 1.0 / 3600 / APPROX_RUN_PER_SEC
					<< " hours on a 2.0Ghz Computer" << endl;
		}
		if (_timeOnly) { return; }; //TODO what this for?
	}
}

//TODO need to overridden in SN Operation!!
double Operation::numPermutations() {
	int i,j;
	double total = 1.0;
	//	if (_opOrder > 2 && t == SN) {
	//		// In this case - we enumerate over groups of 1, 2, N
	//		for (i = 1; i <= m->_groupNum; i++) {
	//			int groupSize = getGroupSize(m, i);
	//			double temp = 0;
	//			double fact = factorial(groupSize);
	//			// Enumerate over the number of groups of size two
	//			for (k = 0; k <= (groupSize / 2); k++) {
	//				// Enumerate over the number of groups of size operationOrder
	//				for (j = 0; j <= ((groupSize - 2 * k) / _opOrder); j++) {
	//					temp += fact/(factorial(groupSize - j * _opOrder - k * 2) *
	//							pow((double)_opOrder, j) *factorial(j) *
	//							pow((double)2, k) * factorial(k));
	//				}
	//			}
	//			total *= (temp);
	//		}
	//	} else {
	assert(_type != SN || _opOrder <=2);
	int groupSize;
	double tmp, fact;
	for (i = 1; i <= _mol->_groupNum; i++) {
		groupSize = _mol->getClassSize(i);
		tmp = 0;
		fact = factorial(groupSize);
		for (j = 0; j <= (groupSize / _opOrder); j++) { //TODO: Don't understand this calculation
			tmp += fact/(factorial(groupSize - j * _opOrder )*pow((double)_opOrder,
					j)*factorial(j));
		}
		total *= (tmp);
	}
	//}
	return total;
}

double Operation::factorial(int n){
	double fact = 1.0;
  	while(n > 0)
    	fact *= n--;
    return fact;
}

void Operation::operate(){
	PermutationGroup pg(_mol);
	cout << "got to operate!!" << endl;

	// calculate csm for each valid permutation & remember minimal (in optimalAntimer)
}



